'use strict';
const fs = require('fs')
const path = require('path')
const inquirer = require('inquirer')//命令交互
const semver = require('semver')
const fse = require('fs-extra')
const ejs = require('ejs')
const glod = require('glob')
const userHome = require('user-home')
const Command = require('@hjx-cli-lerna/command');
const Package = require('@hjx-cli-lerna/package');
const { spinnerStart, execAsync } = require('@hjx-cli-lerna/utils')

const log = require('@hjx-cli-lerna/log')

const getProjectTemplate = require('./getProjectTemplate')

const  TYPE_PROJECT = 'project';
const  TYPE_COMPONENT = 'component';

const TEMPLATE_TYPE_NORMAL = 'normal'
const TEMPLATE_TYPE_CUSTOM = 'custom'

const WHITE_COMMAND = ['npm', 'cnpm']

class InitCommand extends Command {
  init() {
    this.projectName = this._argv[0] || '';
    this.force = !!this._cmd._optionValues.force;
    log.verbose('projectName', this.projectName)
    log.verbose('force', this.force)
  }
  async exec() {
    try {
      // 1、准备阶段
    const projectInfo = await this.prepare();
    if(projectInfo) {
      log.verbose(projectInfo)
      console.log(projectInfo)
      this.projectInfo = projectInfo;
      // 2.下载模板
      await this.downloadTemplate()
      // 3.安装模板
      await this.installTemplate()
    }

    }catch (e){
      log.error(e.message)
      if(process.env.LOG_LEVEL === 'verbose'){
        console.log(e)
      }
    }
  }

  async installTemplate(){
    if(this.templateInfo){
      if(!this.templateInfo.type){
        this.templateInfo.type = TEMPLATE_TYPE_NORMAL;
      }
      if(this.templateInfo.type === TEMPLATE_TYPE_NORMAL){
        await this.installNormalTemplate()
      } else if(this.templateInfo.type === TEMPLATE_TYPE_CUSTOM){
        await this.installCustomTemplate()
      }else {
        throw new Error('无法识别项目模板类型！')
      }
    }else {
      throw new Error('项目模板不存在')
    }
  }

  checkCommand(cmd){
    if(WHITE_COMMAND.includes(cmd)){
      return cmd
    }
    return null
  }

  async execCommand(command, errMsg){
    let ret;
    if(command) {
      const cmdArray = command.split(' ')
      const cmd = this.checkCommand(cmdArray[0]);
      if(!cmd){
        throw new Error('命令不存在！命令'+ command)
      }
      const args = cmdArray.slice(1)
      ret = await execAsync(cmd, args, {
        stdio: 'inherit',
        cwd: process.cwd()
      })
      if(ret !== 0){
        //依赖安装失败
        throw new Error(errMsg)
      }
      return ret
    }
  }

  async ejsRender(options){
    const dir = process.cwd()
    return new Promise((resolve, reject)=>{
      glod('**', {
        cwd: dir,
        ignore: options.ignore || '',
        nodir: true,
      },(err, filers)=>{
        if(err) reject(err)
        Promise.all(filers.map(file=>{
          const filePath = path.join(dir, file);
          return new Promise((resolve1, reject1)=>{
            //模板编译
            ejs.renderFile(filePath, this.projectInfo, {}, (err, result)=>{
              if(err){
                reject1(err)
              }else {
                //重新写入文件
                fse.writeFileSync(filePath, result);
                resolve1(result)
              }
            })
          })
        })).then(()=>{
          resolve()
        }).catch(err=>{
          reject(err)
        })
      })
    })
  }

  async installNormalTemplate(){
    console.log('安装标准模板')
    //拷贝模板代码至当前目录
    let spinner = spinnerStart('正在安装模板...')
    try {
      const templatePath = path.resolve(this.templateNpm.cacheFilePath, 'template');
      const targetPath = process.cwd();
      //确保文件夹存在，如不存在就创建
      fse.ensureDirSync(templatePath)
      fse.ensureDirSync(targetPath)
      fse.copySync(templatePath, targetPath)
    }catch (e){
      throw e
    } finally {
      spinner.stop(true);
      log.success('模板安装成功')
    }
    const templateIgnore = this.templateInfo.ignore || [];
    const ignore = ['**/node_modules/**', ...templateIgnore]
    await this.ejsRender({ignore});
    // 依赖安装
    const { installCommand, startCommand } = this.templateInfo
    await this.execCommand(installCommand, '依赖安装过程中失败！')
    // 启动命令执行
    await this.execCommand(startCommand, '项目启动过程中失败！')
  }
  async installCustomTemplate(){
    const rootFile = this.templateNpm.getRootFilePath();
    if(fs.existsSync(rootFile)){
      log.notice('开始执行自定义模板');
      const templatePath = path.resolve(this.templateNpm.cacheFilePath, 'template');
      const options = {
        templateInfo: this.templateInfo,
        projectInfo: this.projectInfo,
        sourcePath: templatePath,
        targetPath: process.cwd()
      };
      const code = `require('${rootFile}')(${JSON.stringify(options)})`;
      log.verbose('code', code);
      await execAsync('node', ['-e', code], {stdio: 'inherit', cwd: process.cwd()});
      log.success('自定义模板安装成功')
    }else {
      throw new Error('自定义模板入口文件不存在！');
    }
  }

  async downloadTemplate(){
    const  { projectTemplate } = this.projectInfo
    const templateInfo = this.template.find(item => item.npmName === projectTemplate)
    const targetPath = path.resolve(userHome, '.hjx-cli-lerna', 'template');
    const storeDir = path.resolve(userHome, '.hjx-cli-lerna', 'template', 'node_modules')
    const { npmName, version } =  templateInfo
    this.templateInfo = templateInfo
    const templateNpm = new Package({
      targetPath,
      storeDir,
      packageName: npmName,
      packageVersion: version,
    })
    if(! await templateNpm.exists()){
      const spinner = spinnerStart('正在下载模板...')
      try {
        await templateNpm.install();
      }catch (e){
        throw e;
      } finally {
        spinner.stop(true)
        if(await templateNpm.exists()){
          log.success('下载模板成功')
          this.templateNpm = templateNpm
        }
      }
    }else {
      const spinner = spinnerStart('正在更新模板...')
      try {
        await templateNpm.update();
      }catch (e){
        throw e
      } finally {
        spinner.stop(true)
        if(await templateNpm.exists()){
          log.success('更新模板成功')
          this.templateNpm = templateNpm
        }
      }
    }
    // console.log(targetPath, storeDir, npmName, version, templateNpm)

    //1.通过项目模板API获取项目模板信息
    //1.1 通过egg.js搭建一套后端系统
    //1.2 通过npm存储项目模板
    //1.3 将项目模板信息存储到mongodb数据库中
    //1.4 通过egg.js获取mongodb中的数据并且通过API返回
  }
  async prepare(){

    //0.判断项目模板是否存在
    const template = await getProjectTemplate()
    if(!template || template.length === 0){
      throw new Error('项目模板不存在')
    }
    this.template = template

    const localPath = process.cwd();
    //1、当前目录是否为空
    if(!this.isDirEmpty(localPath)){
      let ifContinue = false
      if(!this.force){
        //询问是否继续创建
        ifContinue = (await inquirer.prompt({
          type: 'confirm',
          name: 'ifContinue',
          default: false,
          message: '当前文件夹不为空，是否继续创建项目？'
        })).ifContinue;
        if(!ifContinue) return
      }
      //2、是否启动强制更新
      if(ifContinue || this.force){
        //给用用户做二次确认
        const { confirmDelete } = await inquirer.prompt({
          type: 'confirm',
          name: 'confirmDelete',
          default: false,
          message: '是否清空当前目录下的文件？',
        });
        if(confirmDelete){
          //清空当前目录
          fse.emptyDirSync(localPath);
        }
      }
    }
    return await this.getProjectInfo()
  }

  async getProjectInfo(){
    function isValidName(v){
      return /^[a-zA-Z]+([-][a-zA-Z][a-zA-Z0-9]*|[_][a-zA-Z][a-zA-Z0-9]*|[a-zA-Z0-9])*$/.test(v)
    }
    let projectInfo = {}
    let isProjectNameValid = false;
    if(isValidName(this.projectName)) {
      isProjectNameValid = true
      projectInfo.projectName = this.projectName;
    }
    //1、选择创建项目或组件
    const { type } = await inquirer.prompt({
      type: 'list',
      name: 'type',
      message: '请选择初始化类型',
      default: TYPE_PROJECT,
      choices: [{
        name: '项目',
        value: TYPE_PROJECT
      },{
        name: '组件',
        value: TYPE_COMPONENT,
      }],
    })
    const title = type === TYPE_PROJECT ? '项目' : '组件'
    log.verbose('type', type)
    this.template = this.template.filter(template => template.tag.includes(type))
    const projectNamePrompt = {
      type: 'input',
      name: 'projectName',
      message: `请输入${title}名称`,
      default: '',
      validate: function (v){
        //检查输入的项目名称是否合法
        //1.输入的首字符必须为英文字符
        //2.尾字符必须尾英文或数字
        //3.字符仅允许"-_"
        const done = this.async()
        setTimeout(function (){
          if(!isValidName(v)){
            done(`请输入合法的${title}名称`)
            return
          }
          done(null, true)
        }, 0)
      },
      filter: function (v){
        return v;
      }
    };
    const projectPrompt = [];
    if (!isProjectNameValid){
      projectPrompt.push(projectNamePrompt)
    }
    projectPrompt.push({
      type: 'input',
      name: 'projectVersion',
      message: `请输入${title}版本号`,
      default: '1.0.0',
      validate: function (v){
        //检查输入的版本号是否合法
        const done = this.async()
        setTimeout(function (){
          if(!(!!semver.valid(v))){
            done('请输入合法的版本号')
            return
          }
          done(null, true)
        }, 0)
      },
      filter: function (v){
        if(!!semver.valid(v)){
          return semver.valid(v);
        }else {
          return v
        }

      }
    },{
      type: 'list',
      name: 'projectTemplate',
      message: `请选择${title}模板`,
      choices: this.createTemplateChoice()
    })
    if(type === TYPE_PROJECT){
      //2、获取项目的基本信息
      const project = await inquirer.prompt(projectPrompt)
      projectInfo = {
        ...projectInfo,
        type,
        ...project
      }
    } else if(type === TYPE_COMPONENT){
      const descriptionPrompt = {
        type: 'input',
        name: 'componentDescription',
        message: `请输入${title}描述信息`,
        default: '',
        validate: function (v){
          const done = this.async()
          setTimeout(function (){
            if(!v){
              done('请输入组件描述信息')
              return
            }
            done(null, true)
          }, 0)
        },
      };
      projectPrompt.push(descriptionPrompt);
      //2、获取项目的基本信息
      const component = await inquirer.prompt(projectPrompt)
      projectInfo = {
        ...projectInfo,
        type,
        ...component
      }
    }
    //生成className
    if(projectInfo.projectName){
      projectInfo.name = projectInfo.projectName
      projectInfo.className = require('kebab-case')(projectInfo.projectName).replace(/^-/, '');
    }
    if(projectInfo.projectVersion){
      projectInfo.version = projectInfo.projectVersion
    }
    if(projectInfo.componentDescription){
      projectInfo.description = projectInfo.componentDescription
    }
    //return 项目的基本信息（object）
    return projectInfo
  }

  isDirEmpty(localPath) {
    let fileList = fs.readdirSync(localPath)
    //过滤文件类型
    fileList = fileList.filter(file=>{
      return !file.startsWith('.') && ['node_modules'].indexOf(file) < 0
    });
    return !fileList || fileList.length <= 0
  }
  createTemplateChoice(){
    return this.template.map(item=>{
      return {
        value: item.npmName,
        name: item.name
      }
    })
  }
}
function init(argv) {
  return new InitCommand(argv)
}

module.exports = init

// module.exports.InitCommand = InitCommand;
